﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AhpilyServer
{
    /// <summary>
    /// 服务器端
    /// </summary>
    public  class ServerPeer
    {
        /// <summary>
        /// 赋值服务器端的Socket对象
        /// </summary>
        private Socket serverSoket;//服务器端的Socket对象

        private Semaphore acceptSemaohore; //限制客户端连接数量的信号量
        /// <summary>
        /// 客户端对象的连接池
        /// </summary>
        private ClientPeerPool clientPeerPool;
        /// <summary>
        /// 应用层
        /// </summary>
        private IApplication application;
        /// <summary>
        /// 设置应用层
        /// </summary>
        /// <param name="app"></param>
        public void SetApplication(IApplication app)
        {
            this.application = app;

        }

        /// <summary>
        /// 用来开启服务器
        /// </summary>
        /// <param name="port">端口号</param>
        /// <param name="maxCount">最大连接数量</param>
        public void Start(string ip, int port, int maxCount)
        {
            try
            {
                serverSoket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                acceptSemaohore = new Semaphore(maxCount, maxCount);
                //直接new出最大数量的连接对象
                clientPeerPool = new ClientPeerPool(maxCount);
                ClientPeer tmpClientPeer = null;
                for (int i = 0; i < maxCount; i++)
                {
                    tmpClientPeer = new ClientPeer();
                    tmpClientPeer.ReceiveArgs.Completed += Receive_Completed; //接收完成时触发事件                  
                    tmpClientPeer.receiveCompleted = receiveCompelted;
                    tmpClientPeer.sendDisconnect = Disconnect;
                    clientPeerPool.Enqueue(tmpClientPeer);
                }
                //serverSoket.Bind(new IPEndPoint(IPAddress.Any, port)); //连接地址端口号 任意
                serverSoket.Bind(new IPEndPoint(IPAddress.Parse(ip), port)); //连接地址端口号 任意
                serverSoket.Listen(10);//最大连接数量

                Console.WriteLine("服务器启动 ... ip:{0}   port:{1}", ip, port);
                startAccept(null); //等待客户端连接
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

            }
        }
        #region 接收客户端的连接


        /// <summary>
        /// 等待客户端连接
        /// </summary>
        private void startAccept(SocketAsyncEventArgs e)
        {
            if (e == null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += accept_Completed;
            }


            bool result = serverSoket.AcceptAsync(e); //判断异步连接是否执行完毕 返回T 表示正在执行 执行完毕会触发 返回F表示执行完成 直接处理
            if (result == false)
            {
                processAccept(e);
            }
        }
        /// <summary>
        /// 接受连接请求异步事件完成后触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void accept_Completed(object sender, SocketAsyncEventArgs e)
        {
            processAccept(e);
        }
        /// <summary>
        /// 处理连接请求
        /// </summary>
        /// <param name="e"></param>
        private void processAccept(SocketAsyncEventArgs e)
        {
            // Socket clientSocket = e.AcceptSocket; //得到客户端对象  保存处理 
            // ClientPeer client = new ClientPeer();
            acceptSemaohore.WaitOne(); //计数 限制连接数量 及线程访问 
            ClientPeer client = clientPeerPool.Dequeue(); //取出
            client.ClientSocket = e.AcceptSocket;
            Console.WriteLine("客户端连接成功:" + client.ClientSocket.RemoteEndPoint.ToString());
            startReceive(client); //开始接受数据方法

            e.AcceptSocket = null; //进行保存处理
            startAccept(null);
        }
        #endregion

        #region 接收数据
        /// <summary>
        /// 开始接收数据
        /// </summary>
        /// <param name="client"></param>
        private void startReceive(ClientPeer client)
        {
            try
            {
                bool result = client.ClientSocket.ReceiveAsync(client.ReceiveArgs);
                if (result == false)
                {
                    processAccept(client.ReceiveArgs);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// 处理接受的请求
        /// </summary>
        /// <param name="e"></param>
        private void processReceive(SocketAsyncEventArgs e)
        {
            ClientPeer client = e.UserToken as ClientPeer;
            if (client.ReceiveArgs.SocketError == SocketError.Success && client.ReceiveArgs.BytesTransferred > 0) //判断是否接受成功 且字节数大于0
            {
                byte[] packet = new byte[client.ReceiveArgs.BytesTransferred]; //定义字节长度
                Buffer.BlockCopy(client.ReceiveArgs.Buffer, 0, packet, 0, client.ReceiveArgs.BytesTransferred); //拷贝数据到数组中
                client.StartReceive(packet);//客户端自身处理数据包 自身解析

                startReceive(client); //开启尾递归
            }
            else if (client.ReceiveArgs.BytesTransferred == 0) //断开连接了 没有传输字节 代表断开连接
            {
                if (client.ReceiveArgs.SocketError == SocketError.Success)
                {
                    //客户端主动断开连接
                    Disconnect(client, "客户端主动断开连接");
                }
                else
                {
                    //由于网络异常 导致被动断开连接
                    Disconnect(client, client.ReceiveArgs.SocketError.ToString());
                }

            }


        }
        /// <summary>
        /// 接受完成时 触发的事件
        /// </summary>
        /// <param name="e"></param>
        private void Receive_Completed(object sender, SocketAsyncEventArgs e)
        {
            processReceive(e);
        }
        /// <summary>
        ///  一条数据解析完成的处理
        /// </summary>
        /// <param name="client">对应的连接对象</param>
        /// <param name="value">解析出来的一个具体能使用的类型</param>
        private void receiveCompelted(ClientPeer client, ByteBuffer msg)
        {
            //给应用层 让其使用
            application.OnReceive(client, msg);
        }
        #endregion

        #region 断开连接
        /// <summary>
        /// 
        /// </summary>断开连接
        /// <param name="client">断开的客户端连接对象</param>
        /// <param name="reason">断开的原因</param>
        public void Disconnect(ClientPeer client, string reason)
        {
            try
            {
                if (client == null)
                {
                    throw new Exception("当前指点客户端连接对象为空 无法断开连接");
                }
                Console.WriteLine(client.ClientSocket.RemoteEndPoint.ToString() + "客户端断开连接 原因:" + reason);
                //通知应用层 这个客户端的连接断开了
                application.OnDisconnect(client);
                client.Disconnect();
                clientPeerPool.Enqueue(client); //回收对象 方便下次使用
                acceptSemaohore.Release(); //释放信号量 即限制数量

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

            }
        }
        #endregion



    }
}
